import java.io.File;
import java.io.FileOutputStream;
import java.util.Random;
import java.util.Arrays;


/*
 * Randomized Pearson Correlation
 */
public class ARPC extends Scheduler implements Runnable {
	int start;
	int stop;
	byte[] BLOCK;
	int[] markVoteId;
	byte[] markVote;
	
	final static int N = 150;

	final double NONE = Double.NaN;

	public ARPC(int start, int stop) {
		this.start = start;
		this.stop = stop;
	}

	void writeBlock() {
	    String FILE_NAME = String.format("%s%07d.bin", BLOCK_PATH, start);
	    markVoteId = null;
	    markVote = null;
		File out = new File(FILE_NAME);
		FileOutputStream fos;
		try {
			fos = new FileOutputStream(out);
			fos.write(BLOCK);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("Done writing: " + FILE_NAME + " @ "
				+ this.getClass());
	}

	@Override
	public void run() {
		markVoteId = new int[USERA.length];
		markVote = new byte[USERA.length];
		int ilen = USERB[0].ilen;
		BLOCK = new byte[(stop - start) * ilen];

		int c = 0;
		for (int bUserId = start; bUserId < stop; bUserId++) {
			for (int i = 0; i < ilen; i++) {
				int bItemId = USERB[bUserId].items[i];
				byte pred = predict(bUserId, bItemId);
				BLOCK[c * ilen + i] = pred;
			}
			c++;
		}
		writeBlock();
	}

	byte predict(int bUserId, int bItemId) {
		int[] items = USERA[bUserId].items;
		int ilen = USERA[bUserId].ilen;
		int n = ilen;
		if (n > N) {
			n = N;
		}		
		Item bItem = ITEMA[bItemId];
		for (int userI = 0; userI < bItem.ulen; userI++) {
			markVoteId[bItem.users[userI]] = bItemId;
			markVote[bItem.users[userI]] = bItem.votes[userI];
		}
		Random r = new Random();
		double mostSimilar = 0;
		double mostSimilarVote = USERA[bUserId].mean;
		for (int i = 0; i < n; i++) {
			int idx = r.nextInt(ilen);
			double similarity = Math.abs(pearsonFast(bItemId, items[idx]));
			if (similarity > mostSimilar) {
				mostSimilar = similarity;
				mostSimilarVote = USERA[bUserId].votes[idx];
			}
		}
		
		return (byte) mostSimilarVote;
	}

	double pearson(int iID, int jID) {
		int s = 0;
		double numerator = 0;
		double denominator1 = 0;
		double denominator2 = 0;
		Item i = ITEMA[iID];
		Item j = ITEMA[jID];
		for (int userI = 0; userI < i.ulen; userI++) {
			for (int userJ = s; userJ < j.ulen; userJ++) {
				if (i.users[userI] < j.users[userJ]) {
					s = userJ;
					break;
				} else if (i.users[userI] == j.users[userJ]) {
					double num1 = (USERA[userI].getVoteHash(iID) - i.mean);
					double num2 = (USERA[userJ].getVoteHash(jID) - j.mean);
					numerator += num1 * num2;
					denominator1 += num1 * num1;
					denominator2 += num2 * num2;
				}
			}
		}
		if (denominator1 != 0 && denominator2 != 0) {
			return numerator
					/ (Math.sqrt(denominator1) * Math.sqrt(denominator2));
		}
		return 0;
	}
	
	double pearsonFast(int iID, int jID) {
		double numerator = 0;
		double denominator1 = 0;
		double denominator2 = 0;
		Item i = ITEMA[iID];
		Item j = ITEMA[jID];
		for (int userJ = 0; userJ < j.ulen; userJ++) {
			if (markVoteId[j.users[userJ]] == iID) {
				double num1 = (markVote[j.users[userJ]] - i.mean);
				double num2 = (j.votes[userJ] - j.mean);
				numerator += num1 * num2;
				denominator1 += num1 * num1;
				denominator2 += num2 * num2;
			}
		}
		if (denominator1 != 0 && denominator2 != 0) {
			return numerator
					/ (Math.sqrt(denominator1) * Math.sqrt(denominator2));
		}
		return 0;
	}
}
